13个常用的内置方法

TypeScript在类型检查方面非常强大,但有时,当某些类型是其他类型的子集且你又需要为它们定义类型检查时,TypeScript就会变得冗余。

举个例子,假设有两种响应类型:

我们可以为UserProfileResponse定义类型并为LoginResponse选择属性,而不是定义相同上下文LoginResponseUserProfileResponse的类型。

下面我们将介绍一些可以帮助编写更佳代码的实用程序函数。

Uppercase

构建一个类型,定义其属性全是小写。然后使用Uppercase方法将其全部转化为大写。

Lowercase

与前面一个例子相反,先构建一个属性全是大写的类型,然后使用Lowercase方法将其全部转化为小写。

Capitalize

将所有属性的首字母大写。

Uncapitalize

Capitalize相反,将所有属性取消首字母大写。

Partial

将定义的接口中的属性设置为可选属性。

Required

与Partial相反,Required可以将接口中的可选属性设置为必需。

Readonly

将属性设置为只读。

Record

构造一个对象类型,其属性key是Keys,属性value是Tpye。被用于映射一个类型的属性到另一个类型。

Pick

就是从一个复合类型中,取出几个想要的类型的组合,例如:

Omit

Omit是TypeScript3.5新增的一个辅助类型,它的作用主要是:以一个类型为基础支持剔除某些属性,然后返回一个新类型。

Exclude

将类型中其中一些属性排除,并创建排除属性后的新类型。

Extract

它通过从可分配给联合的类型中提取所有联合成员来创建新类型。

NonNullable

通过从类型中排除nullundefined来创建新类型。

 

8个常用的操作符

例子很简单,但是必须把简单的例子记住,这就不简单了。

1. keyof

作用:从一个对象类型中提取所有键名,组成一个字符串字面量联合类型。

例子:

→ 说明:UserKey 只能是 User 这个类型里真正存在的字段名,防止写错属性名(比如写成 "names" 会报错)。

→ 说明:约束第二个参数 key 必须是 T 真正存在的键,保证运行时 obj[key] 一定能取到值且类型安全。

2. [](索引访问类型)

作用:通过键(可以是字面量或联合类型)从对象类型中取出对应属性的类型。

例子:

→ 说明:精确地取出 primary 这个具体键对应的类型(这里是字面量类型 "#007bff"),而不是更宽泛的 string。

→ 说明:表示“User 对象所有可能的值的类型集合”,常用来描述“任意字段的值”。

3. extends ? :(条件类型)

作用:像三元表达式一样,根据类型是否满足某个条件来选择不同的结果类型。

例子:

好的,我明白你觉得之前的例子太抽象、太“类型编程”了,看起来像谜语一样难懂。

我现在重新给你举 extends 的例子,这次全部用最简单、最日常的场景,像写普通代码一样解释清楚。每个例子都告诉你:

例子1:最简单、最常见的 —— “这个参数必须有 length 属性”

extends 在干什么?
它在告诉 TypeScript:“T 这个类型必须长得像有 length 属性的样子”,不然不让传进来。
没有 extends { length: number },你传数字进来时,thing.length 会直接报错(因为 number 没 length)。

 

例子2:取对象属性的安全函数(超级常用)

extends 在干什么?
K extends keyof T 意思是:key 必须是 person 真正存在的属性名。
如果没有这个 extends,你写 getValue(person, "随便乱写") 也不会报错,但运行时 obj["随便乱写"] 就是 undefined,很危险。

 

例子3:接口继承(写组件 props 时最常见)

extends 在干什么?
让 FancyButtonProps 自动拥有 ButtonProps 里所有的属性,再额外加自己的。 相当于 FancyButtonProps = { text, onClick, color, size },不用重复写。

 

例子4:判断是不是字符串(最基础的条件类型)

extends 在干什么? 像 if 判断:如果 T 是 string,就选左边;否则选右边。 这就是“带 ? :”的经典用法。

 

例子5:去掉函数类型(过滤掉函数)

extends 在干什么? 检查 T 是不是函数:如果是,就返回 never(相当于删除);不是就保留。

总结:你最常遇到的两种 extends(记住这两种就够用了)

  1. 不带 ? : 的(约束)

    • 写法:<T extends XXX>interface Y extends X
    • 意思:必须长这样才行
    • 场景:函数参数限制、接口复用(占 80% 使用量)
  2. 带 ? : 的(条件判断)

    • 写法:T extends XXX ? 是 : 否
    • 意思:如果是这样就...否则就...
    • 场景:类型过滤、类型转换、infer 配合

4. infer(类型推断)

作用:在条件类型中“猜”出(捕获)某个位置的未知类型,把它绑定到一个变量上供后面使用。

例子:

→ 说明:infer U 让 TypeScript 自动推断出数组里元素的真实类型(比如 number[] → infer U 得到 number),然后把 U 作为结果返回。

→ 说明:捕获函数返回值的类型(R),实现类似官方 ReturnType 的功能。

5. in(映射类型中的遍历)

作用:遍历一个联合类型(通常是 keyof 出来的),为每个成员生成一个属性。

例子:

→ 说明:把 Keys 里的每个字符串('loading'|'success'|'error')都变成一个属性名,并且每个属性值都是 boolean 类型,快速生成一组同构的布尔字段。

6. 模板字面量类型 `xxx${T}yyy`

作用:在类型层面进行字符串拼接、组合、转换,生成新的字符串字面量联合类型。

例子:

→ 说明:把 Event 里的每个值(click → Click)首字母大写后拼接上 "on",生成规范的事件处理器名称类型,用于类型安全的字符串匹配。

7. satisfies(TS 4.9+)

作用:对值进行类型检查(必须满足某个类型),但不收窄/丢失原有的更精确的字面量类型。

例子:

→ 说明:强制要求 theme 必须符合后面的形状(有 colors 对象等),但保留了 "#0070f3" 这样的具体字面量类型,而不是退化为宽泛的 string。

不要将satisfies和extends搞混了。虽然中文翻译都好像是“是否满足”,但作用是完全不同的。它们根本不是同一类东西,作用的层面和目的完全不同。简单一句话总结:

层级不同:一个在“类型世界”,一个在“值世界”

8. is(类型谓词 / 类型守卫)

作用:告诉 TypeScript 在某个条件为 true 时,变量的类型可以被收窄成更具体的类型。

例子:

→ 说明:在 if 块内部,input 被 TypeScript 自动收窄为 string,从而允许调用字符串专属的方法,而不会报错。